﻿
using DataViews.Models;

using Microsoft.VisualBasic.FileIO;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;

namespace DataViews.Tools
{


    public class DBTool
    {
        public static IQueryable<T> DoQuery<T>(IQueryable<T> queryable, IEnumerable<FilterItem> filters, string order_by,bool order_desc) where T : class
        {
            IQueryable<T> query = queryable;

            foreach (var item in filters)
            {
                var field = item.FieldName;
                var filter = item.FilterString;
                var field_display = item.FieldDisplayName!;
                try
                {

                    if ((!string.IsNullOrEmpty(filter))&&item.Selected)
                    {                      


                        var field_info = typeof(T).GetProperty(field);
                        var field_type = field_info!.PropertyType;

                        //lumda表达式左边参数
                        var x = Expression.Parameter(typeof(T));
                        //x.xxx
                        var x_field = Expression.PropertyOrField(x, field!);
                        //如果是nullable类型，转换为nullable.value
                        if (field_type.IsGenericType && field_type.GetGenericTypeDefinition() == typeof(Nullable<>))
                        {
                            field_type = field_type.GetGenericArguments()[0];
                            x_field = Expression.PropertyOrField(x_field, "Value");
                        }

                        //得到的lumda表达式
                        Expression<Func<T, bool>> expresstion_lumda = null;



                        if (filter.StartsWith("^"))
                        {
                            if (filter.EndsWith("$"))
                            {
                                //全同匹配
                                filter = filter.Substring(1, filter.Length - 2);

                                //
                                expresstion_lumda = _equal<T>(x, x_field, filter, field_type, field_display);
                            }
                            else
                            {
                                //start_with
                                filter = filter.Substring(1, filter.Length - 1);

                                //
                                expresstion_lumda = _starts_with<T>(x, x_field, filter, field_type, field_display);
                            }
                        }
                        else if (filter.EndsWith("$"))
                        {
                            //ends_with
                            filter = filter.Substring(0, filter.Length - 1);

                            //
                            expresstion_lumda = _ends_with<T>(x, x_field, filter, field_type, field_display);
                        }
                        else if (filter.Contains("*"))
                        {
                            //多个contains
                            var strs = filter.Split('*', StringSplitOptions.RemoveEmptyEntries);

                            //
                            expresstion_lumda = _multi_contains<T>(x, x_field, strs, field_type, field_display);
                        }
                        else if (filter.Contains("#"))
                        {
                            //范围
                            var strs = filter.Split('#', StringSplitOptions.RemoveEmptyEntries);
                            if (strs.Length != 2)
                            {
                                Serilog.Log.Error($"【数据】筛选条件错误: 需要上下限制两个参数  [{item.FieldDisplayName}]={filter}");
                            }

                            //
                            expresstion_lumda = _between<T>(x, x_field, strs, field_type, field_display);

                        }
                        else
                        {
                            //contains
                            expresstion_lumda = _contains<T>(x, x_field, filter, field_type, field_display);
                        }

                        var lumda = expresstion_lumda.Compile();
                        if (query == null)
                        {
                            query = queryable.Where(lumda).AsQueryable();
                        }
                        else
                        {
                            query = query.Where(lumda).AsQueryable();
                        }

                        

                    }
                }
                catch (Exception ex)
                {
                    Serilog.Log.Error($"【数据】筛选条件错误：[{item.FieldDisplayName}]={filter}");
                }



            }

            return query = OrderyBy(query, order_by, order_desc);
        }

        public static IQueryable<T> OrderyBy<T>(IQueryable<T> query, string field, bool desc)
        {
            
            if (string.IsNullOrWhiteSpace(field))
            {
                return query;
            }
            var sortExpression = Expression.Parameter(query.ElementType);
            var selector = Expression.Lambda(Expression.PropertyOrField(sortExpression, field), sortExpression);
            
            if (desc)
            {
                return (IQueryable<T>)query.Provider.CreateQuery(Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { query.ElementType, selector.Body.Type }, query.Expression, selector));
                
            }
            else
            {

                return (IQueryable<T>)query.Provider.CreateQuery(Expression.Call(typeof(Queryable), "OrderBy", new Type[] { query.ElementType, selector.Body.Type }, query.Expression, selector));
            }
            
            

            

        }

        static Expression<Func<T, bool>> _contains<T>(ParameterExpression x, MemberExpression x_field, string filter, Type field_type, string filed_display)
        {
            //
            var str_filter = _GetStringFilter(filter, field_type, filed_display);
            var exp_field_to_string = _GetExpressionFieldToString(x_field, field_type);
            //
            var val = Expression.Constant(str_filter);
            var call = Expression.Call(exp_field_to_string, typeof(string).GetMethod("Contains", new Type[] { typeof(string)})!, val);

            return (Expression<Func<T, bool>>)Expression.Lambda(call, x);
        }

        static Expression<Func<T, bool>> _starts_with<T>(ParameterExpression x, MemberExpression x_field, string filter, Type field_type, string filed_display)
        {
            
            //
            var str_filter = _GetStringFilter(filter, field_type, filed_display);
            var exp_field_to_string = _GetExpressionFieldToString(x_field, field_type);
            //
            var val = Expression.Constant(str_filter);
            var call=Expression.Call(exp_field_to_string, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) })!, val);
            //
            return (Expression<Func<T, bool>>)Expression.Lambda(call, x);
        }

        static Expression<Func<T, bool>> _ends_with<T>(ParameterExpression x, MemberExpression x_field, string filter, Type field_type, string filed_display)
        {           
            //
            var str_filter = _GetStringFilter(filter, field_type, filed_display);
            var exp_field_to_string = _GetExpressionFieldToString(x_field, field_type);
            //
            var val = Expression.Constant(str_filter);
            var call = Expression.Call(exp_field_to_string, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) })!, val);
            //
            return (Expression<Func<T, bool>>)Expression.Lambda(call, x);
            
        }
        static Expression<Func<T, bool>> _between<T>(ParameterExpression x, MemberExpression x_field, string[] filters, Type field_type, string filed_display)
        {
            object obj_filter0 = _GetObjectFilter(filters[0], field_type, filed_display);
            object obj_filter1 = _GetObjectFilter(filters[1], field_type, filed_display);

            var less0 = Expression.LessThanOrEqual(Expression.Constant(obj_filter0), x_field);
            var less1=Expression.LessThanOrEqual(x_field,Expression.Constant(obj_filter1));
            var and=Expression.And(less0, less1);

            return (Expression<Func<T, bool>>)Expression.Lambda(and, x);
        }

        static Expression<Func<T, bool>> _equal<T>(ParameterExpression x, MemberExpression x_field, string filter, Type field_type, string filed_display)
        {
            var obj_filter=_GetObjectFilter(filter, field_type, filed_display);

            var equal=Expression.Equal(x_field,Expression.Constant(obj_filter));
            return (Expression<Func<T, bool>>)Expression.Lambda(equal, x);
        }
        static Expression<Func<T, bool>> _multi_contains<T>(ParameterExpression x, MemberExpression x_field, string[] filters, Type field_type, string filed_display)
        {
            Expression re = null;
            foreach (var filter in filters)
            {
                //
                var str_filter = _GetStringFilter(filter, field_type, filed_display);
                var exp_field_to_string = _GetExpressionFieldToString(x_field, field_type);
                //
                var val = Expression.Constant(str_filter);
                var contains = Expression.Call(exp_field_to_string, typeof(string).GetMethod("Contains", new Type[] { typeof(string) })!, val);

                if (re == null)
                {

                    re = contains;
                }
                else
                {
                    re=Expression.And(re,contains);
                }
            }
            return (Expression<Func<T, bool>>)Expression.Lambda(re!, x);
        }

        static MethodCallExpression _GetExpressionFieldToString(MemberExpression x_field, Type field_type)
        {       
            if (field_type == typeof(DateOnly))
            {
                var format = Expression.Constant("yyyy-MM-dd");
                var method = typeof(DateOnly).GetMethod("ToString", new Type[] { typeof(string) })!;
                return Expression.Call(x_field, method, format);

            }
            else if (field_type == typeof(DateTime) )
            {
                var format = Expression.Constant("yyyy-MM-dd hh:mm:ss");
                var method = typeof(DateTime).GetMethod("ToString", new Type[] { typeof(string) })!;
                return Expression.Call(x_field, method, format);
                
                
            }
            else
            {
                var method = field_type.GetMethod("ToString", new Type[] { })!;
                return Expression.Call(x_field, method, null);

            }

        }

        static string _GetStringFilter(string filter, Type field_type, string field_display)
        {
            if (field_type == typeof(DateOnly) )
            {
                if (filter.Length == 10)
                {
                    if (DateOnly.TryParse(filter, out var date))
                    {
                        return date.ToString("yyyy-MM-dd");
                    }
                }
                Serilog.Log.Warning($"【数据】警告：完整日期格式应为 yyyy-MM-dd [{field_display}]={filter}");

            }
            else if (field_type == typeof(DateTime) )
            {
                if(filter.Length == 19)
                {
                    if (DateTime.TryParse(filter, out var datetime))
                    {
                        return datetime.ToString("yyyy-MM-dd hh:mm:ss");
                    }
                }
                Serilog.Log.Warning($"【数据】警告：完整世界格式应为 yyyy-MM-dd hh:mm:ss [{field_display}]={filter}");
            }
            else
            {
                return filter;
            }

            return filter;
        }

        static object _GetObjectFilter(string filter, Type field_type, string field_display)
        {
            
            try
            {
                if (field_type == typeof(DateOnly))
                {
                    return DateOnly.Parse(filter);
                }
                else if (field_type == typeof(DateTime))
                {
                    return DateTime.Parse(filter);
                }
                else if (field_type == typeof(decimal))
                {
                    return decimal.Parse(filter);
                }
                else if (field_type == typeof(int))
                {
                    return int.Parse(filter);
                }
                else
                {
                    return filter;
                }
                
            }
            catch {
                Serilog.Log.Warning($"【数据】警告：格式无效 [{field_display}]={filter}");
                return filter;
            }
            
        }
    }
}
